package org.springframework.aop.framework.autoproxy;

import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.*;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * 默认的实现动态代理的类
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
    private DefaultListableBeanFactory beanFactory;

    //存放 在解决循环依赖时已经判断过是否要创建代理bean的beanName
    private Set<Object> earlyProxyReferences = new HashSet<>();

    /**
     * 初始化方法之后执行的处理器方法
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //如果bean没有在循环依赖时判断过是否要代理 那么此时就需要判断是否要代理了
        if (!earlyProxyReferences.contains(beanName)) {
            return wrapIfNecessary(bean, beanName);
        }

        //代理了就直接返回 不要重复创建代理对象
        return bean;
    }


    /**
     * 获取代理bean的引用
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        //将已经在循环依赖时创建的代理bean添加到集合里 以免重复判断
        earlyProxyReferences.add(beanName);
        //返回代理bean
        return wrapIfNecessary(bean, beanName);
    }


    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {

        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    //判断是否是切点、通知这些基础的类 这些类不需要判断是否要代理
    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass)
                || Pointcut.class.isAssignableFrom(beanClass)
                || Advisor.class.isAssignableFrom(beanClass);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }


    /**
     * 负责对bean进行判断是否需要代理 如果需要则代理 并且返回代理对象
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    protected Object wrapIfNecessary(Object bean, String beanName) {
        //TODO 避免死循环 已经代理了就直接返回吧
        if (isInfrastructureClass(bean.getClass())) {
            return null;
        }

        //获得所有的Advisor 过滤判断哪个符合当前的bean 符合则代理
        Collection<AspectJExpressionPointcutAdvisor> advisors =
                beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();

        try {
            ProxyFactory proxyFactory = new ProxyFactory();
            for (AspectJExpressionPointcutAdvisor advisor : advisors) {
                //遍历所有的advisor 获取过滤器 判断当前的bean是否需要代理
                ClassFilter classFilter = advisor.getPointcut().getClassFilter();
                //匹配上了则需要代理
                if (classFilter.matches(bean.getClass())) {
                    //为proxyFactory填充属性
                    TargetSource targetSource = new TargetSource(bean);

                    proxyFactory.setTargetSource(targetSource);
                    proxyFactory.addAdvisor(advisor);
                    proxyFactory.setMethodMatcher(advisor.getPointcut().getMethodMatcher());

                    //返回代理对象
                    if (!proxyFactory.getAdvisors().isEmpty()) return proxyFactory.getProxy();
                }
            }
        } catch (Exception ex) {
            throw new BeansException("Error create proxy bean for: " + beanName, ex);
        }
        //不需要代理则返回null
        return null;
    }


    //不做具体的处理
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        return pvs;
    }


}
